package com.dldata.devtools30.repository.common;

import com.dldata.devtools30.cfg.ApplicationHome;
import com.dldata.devtools30.utils.Public;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.security.AnyTypePermission;
import org.springframework.beans.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.stream.Collectors;

/**
 * Created by 呵呵 on 2017-10-20.
 */
public abstract class CommonXmlDao<T> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static ConcurrentSkipListMap<String,ConcurrentSkipListMap> allData = new ConcurrentSkipListMap<String,ConcurrentSkipListMap>();

    protected int maxNumber=Integer.MAX_VALUE;

    private Comparator<Map.Entry<String, T>> entryComparator;

    public CommonXmlDao(){
        try {
            this.entryComparator=createSortComparator();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public CommonXmlDao(int maxNumber){
        try {
            this.entryComparator=createSortComparator();
            this.maxNumber=maxNumber;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<T> getAllCloneData(){
        List<T> collect = getAllData().values().stream().collect(Collectors.toList());
        List<T> result=new LinkedList<>();
        collect.forEach(
                tXml -> {
                    try {
                        T tmpShow=(T)tXml.getClass().newInstance();
                        BeanUtils.copyProperties(tmpShow,tXml);
                        result.add(tmpShow);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        );
        return result;
    }

    public synchronized void refrashAllData() throws Exception {
//        File file= new File(new File(CommonXmlDao.class.getResource("").toURI()),getXmlName());

        File parent = new File(Public.sysConfig.getProperty("projectpath") + "/" + Public.sysConfig.getProperty("projectxmlpath"));
        String tmpPath=parent.getPath();
        if(!parent.exists()){
            parent=new File(ApplicationHome.getApplicationHome()+"/"+Public.sysConfig.getProperty("projectxmlpath"));
            logger.debug("projectpath 或 projectxmlpath配置不正确."+tmpPath+", use " +parent.getPath());
        }
        if(!parent.exists()){
            logger.error("projectpath 或 projectxmlpath配置不正确."+tmpPath+", " +parent.getPath());
            return;
        }
        File file= new File(parent,getXmlName());
        logger.info("refrashAllData: "+file.getPath());
        if(!file.exists()){
            file.createNewFile();
        }
//        InputStream ins= CommonXmlDao.class.getResourceAsStream(getXmlName());
        InputStream ins= new FileInputStream(file);
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
            String tmpStr = null;
            StringBuilder stringBuilder = new StringBuilder();
            while ((tmpStr = reader.readLine()) != null) {
                stringBuilder.append(tmpStr);
                stringBuilder.append("\r\n");
            }
            XStream xstream = createxStream();
            if(stringBuilder.length()==0){
                return;
            }
            Collection<StoreObj<T>> allDataTmp = (Collection<StoreObj<T>>) xstream.fromXML(stringBuilder.toString());
            ConcurrentSkipListMap<String, T> concurrentSkipListMap=new ConcurrentSkipListMap<>();
            allDataTmp.forEach(
                    tStoreObj -> {
                        final T tStoreObjData = tStoreObj.getData();
                        try {
                            Method method= getClassName().getMethod("setId",String.class);
                            method.invoke(tStoreObjData,tStoreObj.getId().toLowerCase());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        concurrentSkipListMap.put(tStoreObj.getId(), tStoreObj.getData());
                    }
            );
            ConcurrentSkipListMap<String, T> allData = getAllData();
            allData.clear();
            allData.putAll(concurrentSkipListMap);
        }finally {
            if(ins!=null){
                ins.close();
            }
        }
    }

    public boolean exists(String id){
        return getAllData().containsKey(id);
    }

    public synchronized void saveAllData() throws Exception {
//        File file= new File(new File(CommonXmlDao.class.getResource("").toURI()),getXmlName());
        File parent = new File(Public.sysConfig.getProperty("projectpath") + "/" + Public.sysConfig.getProperty("projectxmlpath"));
        String tmpPath=parent.getPath();
        if(!parent.exists()){
            parent=new File(ApplicationHome.getApplicationHome()+"/"+Public.sysConfig.getProperty("projectxmlpath"));
            logger.debug("projectpath 或 projectxmlpath配置不正确."+tmpPath+", use " +parent.getPath());
        }
        File file= new File(parent,getXmlName());
        if(!file.exists()){
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        FileOutputStream fileOutputStream = null;
        PrettyPrintWriter prettyPrintWriter =null;
        try{
            LinkedList dataList = new LinkedList();
            getAllData().entrySet().stream().sorted(
                    entryComparator
            ).forEach(
                    stringTEntry -> {
                        StoreObj storeObj = new StoreObj();
                        storeObj.setId(stringTEntry.getKey());
                        storeObj.setData(stringTEntry.getValue());
                        dataList.add(storeObj);
                    }
            );
            XStream xstream = createxStream();
            fileOutputStream=new FileOutputStream(file);
            prettyPrintWriter = new PrettyPrintWriter(new OutputStreamWriter(fileOutputStream,"UTF-8"));
            xstream.marshal(dataList, prettyPrintWriter);
        }
        catch (Exception ex){
            logger.error(ex.getMessage());
            throw new Exception(ex.getMessage());
        }
        finally {
            if(prettyPrintWriter!=null){
                prettyPrintWriter.flush();
            }
            if(prettyPrintWriter!=null){
                prettyPrintWriter.close();
            }
            if(fileOutputStream!=null){
                fileOutputStream.flush();
            }
            if(fileOutputStream!=null){
                fileOutputStream.close();
            }
        }
    }

    private XStream createxStream() {
        XStream xstream = new XStream();
        xstream.alias("DATALIST", LinkedList.class);
        xstream.alias("STOREOBJ", StoreObj.class);
        xstream.alias(getClassName().getSimpleName(),getClassName());
        XStream.setupDefaultSecurity(xstream);
        xstream.setMode(XStream.NO_REFERENCES);
        xstream.addPermission(AnyTypePermission.ANY);
//        xstream.addPermission(NoTypePermission.NONE);
        return xstream;
    }

    public void save(Map<String,T> objects){
        ConcurrentSkipListMap<String, T> allData = getAllData();
        Iterator<Map.Entry<String,T>> keyIter= objects.entrySet().iterator();
        while (keyIter.hasNext()) {
            Map.Entry<String,T> oneValue = keyIter.next();
            if(oneValue.getValue()==null){
                continue;
            }
            allData.remove(oneValue.getKey());
            allData.put(oneValue.getKey(), oneValue.getValue());
        }
        try {
            saveAllData();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public T save(String id,T object){
        ConcurrentSkipListMap<String, T> allData = getAllData();
        T remove = allData.remove(id);
        allData.put(id, object);
        try {
            saveAllData();
            return object;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public T findOne(String id){
       return getAllData().get(id);
    }

    public T delete(String id){
        T remove = getAllData().remove(id);
        try {
            saveAllData();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return remove;
    }

    protected abstract Class<T> getClassName();


    protected abstract Comparator<Map.Entry<String, T>> createSortComparator();

    protected String getXmlName(){
        return  getClassName().getName()+".xml";
    }

    private synchronized ConcurrentSkipListMap<String, T> getAllData() {
        ConcurrentSkipListMap tmpObj=allData.get(getClassName().toString());
        ConcurrentSkipListMap<String, T> realAllData=null;
        if(tmpObj!=null){
            realAllData=(ConcurrentSkipListMap<String, T>)tmpObj;
        }else{
            realAllData=new ConcurrentSkipListMap<String, T>();
            allData.put(getClassName().toString(),realAllData);
        }
        return realAllData;
    }
}
